<script setup lang="ts">
import { ResourceContentVO } from '@/api/resource/content'
import {
  AmbientLight,
  Box3,
  DirectionalLight,
  Object3D,
  PerspectiveCamera,
  Scene,
  Vector3,
  WebGLRenderer
} from "three";
import {TresCanvas} from '@tresjs/core'
import {OrbitControls, useGLTF} from "@tresjs/cientos";

const emit = defineEmits(['delete', 'update'])

const props = defineProps<{
  list: ResourceContentVO[]
}>()

const handleUpdate = (id: number) => {
  emit('update', id)
}

const handleDelete = (id: number) => {
  emit('delete', id)
}

/**
 * 获取模型预览图
 */
const loading = ref(false)
const imgList = ref<string[]>([])
const getModelPreviewImg = async () => {
  loading.value = true
  imgList.value = await Promise.all(props.list.map((item) => loadModel(item.url)))
  loading.value = false
}

/** 加载模型 */
const loadModel = async (url: string) => {
  // 配置基础设施
  const scene = new Scene()
  const camera = new PerspectiveCamera()

  const renderer = new WebGLRenderer({
    antialias: true,
    preserveDrawingBuffer: true,
    alpha: true,
  })

  scene.background = null
  renderer.setSize(512, 512)

  try {
    // 加载模型
    const {scene: model} = await useGLTF(url, {draco: true})
    scene.add(model)

    // 设置相机位置
    const box = new Box3().setFromObject(model)
    const center = box.getCenter(new Vector3())
    const size = box.getSize(new Vector3()).length()

    camera.position.copy(center)
    camera.position.z += size * 1.5
    camera.lookAt(center)

    // 添加基础光照
    const ambientLight = new AmbientLight(0xffffff, 0.5);
    const directionalLight = new DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(5, 5, 5);
    scene.add(ambientLight, directionalLight);

    // 渲染单帧
    renderer.render(scene, camera)

    // 转成图片
    return renderer.domElement.toDataURL("image/png")
  } finally {
    // 清理资源
    scene.clear()
    renderer.dispose()
  }
}

/** 预览模型 **/
const dialogVisible = ref(false)
const dialogLoading = ref(false)
const model = shallowRef<Object3D | undefined>()
const previewModel = async (url: string) => {
  dialogVisible.value = true
  dialogLoading.value = true

  const {scene} = await useGLTF(url, {draco: true})
  model.value = scene

  dialogLoading.value = false
}

onMounted(async () => {
  await getModelPreviewImg()
})

</script>

<template>
  <el-row v-loading="loading">
    <el-col v-for="(item, index) in list" :key="item.id" :span="3">
      <el-card class="card">
        <template #header>
          <div class="img h-48 w-48">
            <el-image
              :src="imgList[index]"
              fit="cover"
              @click="previewModel(item.url)"
            />
          </div>
        </template>

        <el-space direction="vertical" alignment="normal">
          <el-text size="large">{{ item.name }}</el-text>
          <el-text type="info">{{ item.remark }}</el-text>
          <div>
            <el-button type="primary" link @click="handleUpdate(item.id)">修改</el-button>
            <el-button type="danger" link @click="handleDelete(item.id)">删除</el-button>
          </div>
        </el-space>
      </el-card>
    </el-col>
  </el-row>

  <Dialog v-model="dialogVisible" title="预览模型" width="800">
    <div style="height: auto; min-height: 600px" v-loading="dialogLoading">
      <TresCanvas>
        <TresPerspectiveCamera />
        <TresAmbientLight :intensity="1" />
        <OrbitControls make-default />

        <primitive v-if="!dialogLoading" :object="model" />
      </TresCanvas>
    </div>
  </Dialog>
</template>

<style scoped lang="scss">
.card {
  margin: 16px;

  :deep(.el-card__header) {
    padding: 4px;

    display: flex;
    align-items: center;
    justify-content: center;
  }

  :deep(.el-card__body) {
    padding: 16px;
  }
}

img {
  -webkit-user-drag: none;
}
</style>
